package com.google.gwt.mobile.widgets.transition;

import com.google.gwt.mobile.widgets.resource.Resources;
import com.google.gwt.mobile.widgets.ui.TransitionWidget;
import com.google.gwt.mobile.widgets.utils.Utils;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.HasWidgets;

/**
 * The Class Transition.
 */
public class Transition implements EventListener {

  TransitionWidget _from, _to;
  String _transitionStyleName;
  boolean _reverse;
  HasWidgets _parent;

  public static Transition SLIDE = new Transition(Resources.TRANSITIONS.slide());
  public static Transition SLIDEUP = new Transition(Resources.TRANSITIONS.slideup());
  public static Transition SLIDEDOWN = new Transition(Resources.TRANSITIONS.slidedown());
  public static Transition FADE = new Transition(Resources.TRANSITIONS.fade());
  public static Transition POP = new Transition(Resources.TRANSITIONS.pop());
  public static Transition FLIP = new FlipTransition();
  public static Transition SWAP = new SwapTransition();

  /**
   * Instantiates a new transition.
   * 
   * @param transitionStyleName the transition style name
   */
  public Transition(final String transitionStyleName) {
    _transitionStyleName = transitionStyleName;
  }

  /**
   * Start.
   * 
   * @param from the from
   * @param to the to
   * @param parent the parent
   */
  public static void start(final TransitionWidget from, final TransitionWidget to,
      final HasWidgets parent) {
    new Timer() {
      @Override
      public void run() {
        parent.remove(from);
        parent.add(to);
        to.onTransitionEnd();
      }
    }.schedule(1);
  }

  /**
   * Start.
   * 
   * @param from the from
   * @param to the to
   * @param parent the parent
   * @param reverse the reverse
   */
  public void start(final TransitionWidget from, final TransitionWidget to,
      final HasWidgets parent, final boolean reverse) {
    _from = from;
    _to = to;
    _parent = parent;
    _reverse = reverse;
    prepare();
    start();
  }

  protected void prepare() {
    _from.addStyleName(_transitionStyleName + " " + Resources.TRANSITIONS.out());
    _to.addStyleName(_transitionStyleName + " " + Resources.TRANSITIONS.in());
    if (_reverse) {
      _from.addStyleName(Resources.TRANSITIONS.reverse());
      _to.addStyleName(Resources.TRANSITIONS.reverse());
    }
    _parent.add(_to);
  }

  protected void start() {
    registerTransitionEndEvent();

    new Timer() {
      @Override
      public void run() {
        _from.addStyleName(Resources.TRANSITIONS.start());
        _to.addStyleName(Resources.TRANSITIONS.start());
      }
    }.schedule(20); // xxms instead of 1ms, to give iOS/Android enough time to
    // set the starting state.

  }

  protected void removeTransitionStyles() {
    _from.removeStyleName(_transitionStyleName);
    _from.removeStyleName(Resources.TRANSITIONS.start());
    _from.removeStyleName(Resources.TRANSITIONS.out());
    _from.removeStyleName(Resources.TRANSITIONS.reverse());
    _to.removeStyleName(_transitionStyleName);
    _to.removeStyleName(Resources.TRANSITIONS.in());
    _to.removeStyleName(Resources.TRANSITIONS.start());
    _to.removeStyleName(Resources.TRANSITIONS.reverse());
  }

  /*
   * (non-Javadoc)
   * 
   * @see
   * com.google.gwt.user.client.EventListener#onBrowserEvent(com.google.gwt.
   * user.client.Event)
   */
  @Override
  public void onBrowserEvent(final Event e) {
    String type = e.getType();
    if (type.equals("webkitTransitionEnd") || type.equals("webkitAnimationEnd")) {
      onTransitionEnd();
    }
  }

  protected void onTransitionEnd() {
    if (_from != null && _to != null) {
      _parent.remove(_from);
      removeTransitionStyles();
      _to.onTransitionEnd();
      _from = null;
      _to = null;
      _parent = null;
    }
  }

  protected void registerTransitionEndEvent() {
    if (!_reverse) {
      Utils.setEventListener(_to.getElement(), "webkitTransitionEnd", false, this);
    } else {
      Utils.setEventListener(_from.getElement(), "webkitTransitionEnd", false, this);
    }
  }

  private static class FlipTransition extends Transition {
    private int _phase = 0;

    FlipTransition() {
      super(Resources.TRANSITIONS.flip0());
    }

    @Override
    protected void registerTransitionEndEvent() {
      if (_phase == 0) {
        Utils.setEventListener(_from.getElement(), "webkitTransitionEnd", false, this);
      } else {
        Utils.setEventListener(_to.getElement(), "webkitTransitionEnd", false, this);
      }
    }

    @Override
    protected void onTransitionEnd() {
      removeTransitionStyles();
      if (_phase == 0) {
        _parent.remove(_from);
        _parent.add(_to);
        _phase++;
        _transitionStyleName = Resources.TRANSITIONS.flip1();
        prepare();
        start();
      } else {
        _to.onTransitionEnd();
        _from = null;
        _to = null;
        _phase = 0;
        _transitionStyleName = Resources.TRANSITIONS.flip0();
      }
    }

    @Override
    protected void prepare() {
      _from.addStyleName(_transitionStyleName + " " + Resources.TRANSITIONS.out());
      _to.addStyleName(_transitionStyleName + " " + Resources.TRANSITIONS.in());
      if (_reverse) {
        _from.addStyleName(Resources.TRANSITIONS.reverse());
        _to.addStyleName(Resources.TRANSITIONS.reverse());
      }
    }

  }

  private static class SwapTransition extends Transition {
    private int _phase = 0;

    SwapTransition() {
      super(Resources.TRANSITIONS.swap0());
    }

    @Override
    protected void onTransitionEnd() {
      removeTransitionStyles();
      if (_phase == 0) {
        _phase++;
        _transitionStyleName = Resources.TRANSITIONS.swap1();
        prepare();
        start();
      } else {
        _parent.remove(_from);
        _to.onTransitionEnd();
        _from = null;
        _to = null;
        _phase = 0;
        _transitionStyleName = Resources.TRANSITIONS.swap0();
      }
    }

    @Override
    protected void prepare() {
      _from.addStyleName(_transitionStyleName + " " + Resources.TRANSITIONS.out());
      _to.addStyleName(_transitionStyleName + " " + Resources.TRANSITIONS.in());
      if (_reverse) {
        _from.addStyleName(Resources.TRANSITIONS.reverse());
        _to.addStyleName(Resources.TRANSITIONS.reverse());
      }
      if (_phase == 0) {
        _parent.add(_to);
      }
    }
  }
}
